# 兼容性处理：解决urllib3版本与OpenSSL版本冲突问题
try:
    import requests
except ImportError as e:
    if "urllib3" in str(e) and "OpenSSL" in str(e):
        print("检测到urllib3版本与OpenSSL版本不兼容")
        print("请执行以下命令解决：")
        print("pip install 'urllib3<2.0'")
        print("或者：")
        print("pip install requests urllib3==1.26.18")
        exit(1)
    else:
        raise e

import time
import json
import base64
import os
import logging
from datetime import datetime
import schedule

# 禁用urllib3的SSL警告（针对旧版本OpenSSL）
try:
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except:
    pass

# 导入AES加密库
try:
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import unpad
    from Crypto.Util.Padding import pad
except ImportError:
    # 尝试使用另一种常见的AES库导入方式
    try:
        from Cryptodome.Cipher import AES
        from Cryptodome.Util.Padding import unpad
        from Cryptodome.Util.Padding import pad
    except ImportError:
        print("错误: 未找到AES加密库。请安装pycryptodome或pycryptodomex")
        raise

# 配置日志
def setup_logging():
    """
    配置日志系统，输出到monitor.log文件
    """
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler('monitor.log', encoding='utf-8'),
            logging.StreamHandler()  # 同时输出到控制台
        ]
    )
    return logging.getLogger(__name__)

# 获取Linux系统的User-Agent
def get_user_agent():
    """
    返回Linux系统的User-Agent
    """
    return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"

# 配置信息
CONFIG = {
    "api_urls": {
        "device_info": "http://jdt.gzjiediantong.com/MonitorChargeStation/ac_searchDeviceInfos",
        "charge_progress": "http://jdt.gzjiediantong.com/MonitorChargeStation/ac_loadStationTimeInfos"
    },
    "target_server_urls": {
        "device_info": "http://127.0.0.1:8080/api/device/info",
        "charge_progress": "http://127.0.0.1:8080/api/device/charge-info"
    },
    "domain": "jdt.gzjiediantong.com",
    "request_interval": 30,  # 请求间隔（秒）
    "headers": {
        "accept": "*/*",
        "accept-encoding": "gzip, deflate",
        "accept-language": "zh-CN,zh;q=0.9",
        "cache-control": "no-cache",
        "connection": "keep-alive",
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "pragma": "no-cache",
        "referer": "http://jdt.gzjiediantong.com/monitorchargestation/index",
        "user-agent": get_user_agent(),
        "x-requested-with": "XMLHttpRequest"
    },
    # Cookie文件路径
    "cookie_file": "cookie.txt"
}

def load_cookies_from_file(cookie_file):
    """
    从本地文件加载cookie
    文件格式示例：
    p_suffix=jdt; Hm_lvt_25b75d892cfb2fd9fc2b3c4e5031aed7=1755403816,1757214929; aes_key=B613F1643DBE76CE; p_platutoken=8CAFC1C113279806308C5AB07F1651A2-2D8E00;
    """
    logger = logging.getLogger(__name__)
    logger.info(f"Cookie加载信息 - Cookie文件路径: {cookie_file}")
    
    try:
        if not os.path.exists(cookie_file):
            logger.error(f"Cookie文件 {cookie_file} 不存在")
            logger.info("请创建 cookie.txt 文件并填入有效的cookie内容")
            logger.info("文件格式示例：p_suffix=jdt; aes_key=B613F1643DBE76CE; p_platutoken=8CAFC1C113279806308C5AB07F1651A2-2D8E00;")
            return {}
            
        with open(cookie_file, 'r', encoding='utf-8') as f:
            cookie_content = f.read().strip()
            
        if not cookie_content:
            logger.error(f"Cookie文件 {cookie_file} 为空")
            return {}
            
        # 解析cookie字符串
        cookies = {}
        cookie_pairs = cookie_content.split(';')
        
        for pair in cookie_pairs:
            pair = pair.strip()
            if '=' in pair:
                name, value = pair.split('=', 1)
                cookies[name.strip()] = value.strip()
                
        logger.info(f"成功加载cookie，共{len(cookies)}个cookie")
        logger.info(f"获取的cookie名称: {', '.join(cookies.keys())}")
        
        # 检查必要的cookie
        required_cookies = ["aes_key", "p_platutoken", "PLAY_SESSION"]
        missing_cookies = [cookie for cookie in required_cookies if cookie not in cookies]
        if missing_cookies:
            logger.warning(f"缺少必要的cookie: {', '.join(missing_cookies)}")
            logger.info("请确保在cookie.txt文件中包含这些cookie")
        else:
            logger.info("所有必要的cookie都已找到")
            
        return cookies
        
    except Exception as e:
        logger.error(f"加载cookie文件时发生错误: {e}")
        import traceback
        logger.error(traceback.format_exc())
        return {}

def aes_decrypt(data, key):
    """
    AES解密函数
    使用ECB模式和Pkcs7填充
    """
    logger = logging.getLogger(__name__)
    try:
        # 确保data是字符串
        if isinstance(data, bytes):
            data = data.decode('utf-8')
            
        # 将密钥转换为UTF-8编码的字节
        key_bytes = key.encode('utf-8')
        
        # 确保密钥长度符合AES要求（16、24或32字节）
        # 如果密钥长度不足，使用PKCS#7填充到16字节
        if len(key_bytes) not in (16, 24, 32):
            key_bytes = key_bytes.ljust(16, b'\0')[:16]  # 调整为16字节
            logger.warning("密钥长度调整为16字节以兼容AES-128")
            
        # 对base64编码的数据进行解码
        try:
            encrypted_data = base64.b64decode(data)
        except Exception as base64_e:
            logger.error(f"Base64解码失败: {base64_e}")
            # 尝试处理可能有额外字符的数据
            import re
            clean_data = re.sub(r'[^A-Za-z0-9+/=]', '', data)
            encrypted_data = base64.b64decode(clean_data + '=' * ((4 - len(clean_data) % 4) % 4))
            
        # 创建AES解密器
        cipher = AES.new(key_bytes, AES.MODE_ECB)
        
        # 解密并去除填充
        try:
            decrypted_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)
        except ValueError:
            # 如枟解密后的数据不是标准的PKCS#7填充格式，尝试直接解密
            logger.warning("标准PKCS#7填充解析失败，尝试直接解密")
            decrypted_data = cipher.decrypt(encrypted_data)
            # 尝试去除可能的NULL填充
            decrypted_data = decrypted_data.rstrip(b'\0')
        
        # 转换为字符串并返回
        try:
            return decrypted_data.decode('utf-8')
        except UnicodeDecodeError:
            # 尝试使用其他编码格式
            try:
                return decrypted_data.decode('gbk')
            except UnicodeDecodeError:
                logger.warning("无法解码解密后的数据为UTF-8或GBK格式")
                # 返回原始字节的十六进制表示，便于调试
                return decrypted_data.hex()
    except Exception as e:
        logger.error(f"AES解密失败: {e}")
        import traceback
        logger.error(traceback.format_exc())
        return None

def request_api(url, cookies, params=None):
    """
    请求API接口并返回解密后的数据
    针对Linux系统优化
    """
    logger = logging.getLogger(__name__)
    try:
        # 复制基础 headers 并添加 cookie
        headers = CONFIG["headers"].copy()
        headers["cookie"] = "; ".join([f"{k}={v}" for k, v in cookies.items()])
        
        # Linux系统配置
        proxies = None
        verify_ssl = True
        timeout = 30
        
        logger.info("在Linux环境下请求API")
            
        # 如果没有提供参数，使用空字典
        if params is None:
            params = {}
            
        # 打印完整的请求URL和参数
        print(f"\n[API请求信息]")
        print(f"请求URL: {url}")
        print(f"请求参数: {params}")
        
        # 发送请求，设置超时和重试策略
        try:
            response = requests.get(
                url, 
                headers=headers, 
                timeout=timeout,
                proxies=proxies,
                verify=verify_ssl
            )
            response.raise_for_status()
        except requests.exceptions.Timeout:
            print(f"请求超时: {url}")
            # 尝试再次请求
            try:
                print("尝试再次请求...")
                response = requests.get(
                    url, 
                    headers=headers, 
                    timeout=60,
                    proxies=proxies,
                    verify=verify_ssl
                )
                response.raise_for_status()
            except Exception as retry_e:
                print(f"再次请求失败: {retry_e}")
                return None
        except Exception as request_e:
            print(f"请求API失败: {request_e}")
            return None

        # 解析响应
        try:
            result = response.json()
        except json.JSONDecodeError:
            print(f"响应不是有效的JSON格式")
            # 尝试处理可能的BOM头
            try:
                content = response.text
                if content.startswith('\ufeff'):
                    content = content[1:]
                result = json.loads(content)
            except Exception as json_e:
                print(f"解析JSON失败: {json_e}")
                # 打印响应内容，便于调试
                print(f"原始响应内容: {response.text[:200]}...")
                return None

        # 检查是否需要解密
        if result.get("isEncrypt"):
            print("[加密响应信息]")
            print(f"加密标记: {result.get('isEncrypt')}")
            print(f"是否包含加密数据: {'data' in result}")
            print(f"加密数据完整内容: {str(result.get('data'))}")
            
            # 获取aes_key
            aes_key = cookies.get("aes_key")
            if not aes_key:
                print("未找到aes_key")
                # 尝试从result中直接获取数据
                if "data" in result:
                    print("尝试直接使用未解密的数据")
                    return result
                return None

            # 解密数据
            decrypted_data = aes_decrypt(result["data"], aes_key)
            if decrypted_data:
                print("[解密后响应信息]")
                print(f"解密数据完整内容: {decrypted_data}")
                try:
                    decrypted_json = json.loads(decrypted_data)
                    print(f"解密数据是否为有效JSON: 是")
                    print(f"解密后数据结构: {list(decrypted_json.keys()) if isinstance(decrypted_json, dict) else '非字典类型'}")
                    return decrypted_json
                except json.JSONDecodeError:
                    print("解密后的数据不是有效的JSON格式")
                    # 返回原始解密字符串，便于调试
                    return {"raw_decrypted_data": decrypted_data}
            else:
                print("解密失败，但返回原始加密数据")
                return result
        else:
            # 非加密响应，直接打印完整内容
            print("[非加密响应信息]")
            print(f"响应数据完整内容: {json.dumps(result, ensure_ascii=False)}")
            print(f"响应数据结构: {list(result.keys()) if isinstance(result, dict) else '非字典类型'}")
            return result
    except Exception as e:
        print(f"请求API时发生未捕获异常: {e}")
        import traceback
        traceback.print_exc()
        return None

def send_to_target_server(data, url_type='device_info'):
    """
    将数据发送到目标服务器
    针对Linux系统优化
    
    参数:
    data: 要发送的数据
    url_type: 目标URL类型 ('device_info' 或 'charge_progress')
    """
    logger = logging.getLogger(__name__)
    logger.info("发送数据到目标服务器")
    try:
        if not data:
            logger.warning("没有数据可发送")
            return False

        # 获取对应的目标服务器URL
        target_url = CONFIG['target_server_urls'].get(url_type, CONFIG['target_server_urls']['device_info'])
        logger.info(f"目标服务器: {target_url}")
        logger.info(f"数据大小: {len(str(data))} 字符")
        logger.info(f"数据结构: {list(data.keys()) if isinstance(data, dict) else '非字典类型'}")

        # 设置请求参数
        headers = {
            "Content-Type": "application/json",
            "User-Agent": get_user_agent()
        }
        verify_ssl = True
        proxies = None
        timeout = 30

        logger.info("在Linux环境下发送数据")

        # 尝试发送数据
        retries = 3  # 重试次数
        retry_count = 0
        while retry_count <= retries:
            try:
                response = requests.post(
                    target_url,
                    headers=headers,
                    json=data,
                    timeout=timeout,
                    proxies=proxies,
                    verify=verify_ssl
                )
                response.raise_for_status()
                logger.info(f"数据发送成功: {response.status_code}")
                logger.info(f"响应内容: {response.text}")
                return True
            except requests.exceptions.Timeout:
                retry_count += 1
                if retry_count > retries:
                    logger.error(f"发送数据超时，已重试{retries}次，失败")
                    return False
                logger.warning(f"发送数据超时，等待{retry_count*2}秒后重试 ({retry_count}/{retries})")
                time.sleep(retry_count*2)
            except requests.exceptions.ConnectionError:
                retry_count += 1
                if retry_count > retries:
                    logger.error(f"连接目标服务器失败，已重试{retries}次，失败")
                    return False
                logger.warning(f"连接目标服务器失败，等待{retry_count*2}秒后重试 ({retry_count}/{retries})")
                time.sleep(retry_count*2)
            except Exception as e:
                logger.error(f"发送数据到目标服务器失败: {e}")
                return False
    except Exception as e:
        logger.error(f"发送数据时发生未捕获异常: {e}")
        import traceback
        logger.error(traceback.format_exc())
        return False

def job():
    """
    定时执行的任务
    针对Linux系统优化
    """
    logger = logging.getLogger(__name__)
    logger.info(f"执行任务: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

    try:
        logger.info("当前环境: Linux系统")

        # 获取cookie
        cookies = load_cookies_from_file(CONFIG["cookie_file"])
        
        # 检查cookie获取结果
        if not cookies:
            logger.error("严重错误：未获取到任何cookie！")
            logger.info("请按照以下步骤解决：")
            logger.info("1. 确保 cookie.txt 文件存在且包含有效内容")
            logger.info("2. 文件格式参考：p_suffix=jdt; aes_key=B613F1643DBE76CE; p_platutoken=8CAFC1C113279806308C5AB07F1651A2-2D8E00;")
            logger.info("3. 特别注意：必须包含aes_key和p_platutoken这两个关键cookie")
            logger.info("任务终止，请解决cookie问题后重试")
            return

        # 检查是否包含必要的cookie
        required_cookies = ["aes_key", "p_platutoken", "PLAY_SESSION"]
        missing_cookies = [cookie for cookie in required_cookies if cookie not in cookies]
        if missing_cookies:
            logger.warning(f"缺少必要的cookie: {', '.join(missing_cookies)}")
            logger.warning("这些cookie对于API请求的成功至关重要！")
            logger.info("请确保在cookie.txt文件中包含了所有必要的cookie")
            logger.info("尝试使用当前可用的cookie继续执行，但可能会导致API请求失败")
            
        # 记录当前可用的cookie
        logger.info(f"当前可用的cookie数量: {len(cookies)}")
        logger.info(f"包含aes_key: {'aes_key' in cookies}")

        # 创建info参数，包含必要的信息
        info_param = {
            "stationId": "6403",
            "areaId": "",
        }
        
        # 检查stationId是否已配置
        if info_param["stationId"] == "YOUR_STATION_ID":
            logger.warning("stationId仍为默认值！")
            logger.info("请按照以下步骤操作：")
            logger.info("1. 在浏览器中登录 http://jdt.gzjiediantong.com")
            logger.info("2. 打开开发者工具(F12)，查看Network请求中的参数")
            logger.info("3. 查找stationId的实际值并更新到代码中")
            logger.info("4. 这是API请求成功的必要条件！")
            logger.info("根据接口参数，areaId应设置为空字符串")

        # 请求充电桩信息
        try:
            current_timestamp = int(time.time() * 1000)
            api_url_with_params = f"{CONFIG['api_urls']['device_info']}?t={current_timestamp}&info[stationId]={info_param['stationId']}&info[areaId]={info_param['areaId']}"
            device_info = request_api(api_url_with_params, cookies, None)
            # 检查是否包含错误码
            if device_info and isinstance(device_info, dict) and device_info.get('code') == 9999:
                logger.warning(f"充电桩信息请求结果: 接口调用成功，但返回系统异常: {device_info.get('msg')}")
            else:
                logger.info(f"充电桩信息请求结果: {'成功' if device_info else '失败'}")
        except Exception as e:
            logger.error(f"请求充电桩信息时发生异常: {e}")
            device_info = None

        # 请求充电进度信息
        try:
            current_timestamp = int(time.time() * 1000)
            api_url_with_params = f"{CONFIG['api_urls']['charge_progress']}?t={current_timestamp}&stationId={info_param['stationId']}&info[stationId]={info_param['stationId']}&info[areaId]={info_param['areaId']}"
            charge_progress = request_api(api_url_with_params, cookies, None)
            logger.info(f"充电进度信息请求结果: {'成功' if charge_progress else '失败'}")
        except Exception as e:
            logger.error(f"请求充电进度信息时发生异常: {e}")
            charge_progress = None

        # 发送到目标服务器
        if device_info and isinstance(device_info, dict) and 'data' in device_info:
            logger.info(f"发送解密后的data属性到目标服务器，数据大小: {len(str(device_info['data']))} 字符")
            send_to_target_server(device_info['data'], 'device_info')
        else:
            logger.warning("没有获取到有效的设备信息data属性，不发送到目标服务器")
            
        if charge_progress and isinstance(charge_progress, dict) and 'data' in charge_progress:   
            logger.info(f"发送解密后的data属性到目标服务器，数据大小: {len(str(charge_progress['data']))} 字符")
            send_to_target_server(charge_progress['data'], 'charge_progress')
        else:
            logger.warning("没有获取到充电进度信息，不发送到目标服务器")
            
    except Exception as e:
        logger.error(f"任务执行过程中发生未捕获异常: {e}")
        import traceback
        logger.error(traceback.format_exc())
        # 确保任务不会中断，记录错误后继续
        logger.info("任务异常，但会继续执行下一轮")

def main():
    """
    主函数
    针对Linux系统优化
    """
    # 初始化日志系统
    logger = setup_logging()
    
    logger.info("充电桩监控程序启动")
    logger.info("当前系统: Linux")
    logger.info("已应用Linux系统优化配置")
    
    # 打印配置信息摘要
    logger.info(f"请求间隔: {CONFIG['request_interval']}秒")
    logger.info(f"Cookie文件: {CONFIG['cookie_file']}")
    logger.info(f"目标服务器 - 设备信息: {CONFIG['target_server_urls']['device_info']}")
    logger.info(f"目标服务器 - 充电进度: {CONFIG['target_server_urls']['charge_progress']}")
    logger.info(f"监控的API - 设备信息: {CONFIG['api_urls']['device_info']}")
    logger.info(f"监控的API - 充电进度: {CONFIG['api_urls']['charge_progress']}")

    # 设置定时任务
    logger.info(f"设置定时任务，每{CONFIG['request_interval']}秒执行一次")
    schedule.every(CONFIG['request_interval']).seconds.do(job)
    
    # 运行定时任务
    try:
        while True:
            schedule.run_pending()
            time.sleep(1)
    except KeyboardInterrupt:
        logger.info("用户中断程序，充电桩监控任务已停止")

if __name__ == "__main__":
    main()